package main

import (
	"fmt"
)

/*
在 Golang 的世界里没有继承，只有组合和接口，并且是松散的接口结构，不强制声明实现接口
GO 语言的结构体 struct 是组合非继承，不像其它 OOP 语言那样通过继承机制实现类结构的扩展。
*/

//定义interface
type VowelsFinder interface {
	FindVowels() []rune
}

type MyString string

// 实现接口
func (ms MyString) FindVowels() []rune {
	var vowels []rune
	for _, rune := range ms {
		if rune == 'a' || rune == 'e' || rune == 'i' || rune == 'o' || rune == 'u' {
			vowels = append(vowels, rune)
		}
	}
	return vowels
}

type Myage string

// 实现接口
func (ms Myage) FindVowels() []rune {
	var vowels []rune
	for _, rune := range ms {
		if rune == 'a' || rune == 'e' || rune == 'i' || rune == 'o' || rune == 'u' {
			vowels = append(vowels, rune)
		}
	}
	return vowels
}
func main() {
	Test2()
}

func Test1() {
	name := MyString("Sam Anderson") // 类型转换
	var v VowelsFinder               // 定义一个接口类型的变量
	v = name
	fmt.Printf("Vowels are %c", v.FindVowels())

	age := Myage("abcdefghigklmn")
	fmt.Println()

	var g VowelsFinder
	g = age
	fmt.Println("Vowels are：", g.FindVowels())
}

/*
Being 是最基础的结构体，Human 组合了 Being，而 Student 又组合了 Human。 但是通过 Human.Eat() 方法调用 Drink() 只能是 Human.Drink()。
如果 Student 也实现了 Eat 方法并调用 Dranking()，则会调用自己的 Student.Dranking()。

对 s 分别进行了 Human、Student 类型断言，输出结果可以看到，s 断言 Human 是不成功的，因为它是 Student 类型。断言 IHuman 接口也是成功的，
因为 Student 结构实现了 IHuman 接口的所以方法。也就是说看起来叫起来都像鸭子的东西，那就可以认为它是鸭子

描述事物的外部行为而非内部结构

go语言通过interface变量实现的迟后绑定，称之为“鸭子类型”。
同一个interface变量可以当做不同类型的变量--只要它们都实现了那个interface类型规定的方法Quack。
*/

func Test2() {
	var h Human

	s := Studentt{Grade: 1, Human: Human{Name: "Jason", Age: 12, Being: Being{IsLive: true}}}
	fmt.Println("student:", s)
	fmt.Println("student ", s.Name, " is alive:", s.IsLive)

	// h = s // cannot use s (type Student) as type Human in assignment
	fmt.Println(h)

	// Heal(s) // cannot use s (type Student) as type Being in argument to Heal
	Heal(s.Human.Being) // true

	s.Drink() // student drinking...
	s.Eat()   // human eating... & human drinking...

	human, b := interface{}(s).(Human) // false s is Student but Human
	fmt.Println(human, b)

	student, b := interface{}(s).(Studentt)
	fmt.Println(student, b)

	ihuman, b := interface{}(s).(IHuman)
	fmt.Println(ihuman, b)

}

type Being struct {
	IsLive bool
}

type Human struct {
	Being
	Name string
	Age  int
}

func (h Human) Eat() {
	fmt.Println("human eating...")
	h.Drink()
}

func (h Human) Drink() {
	fmt.Println("human drinking...")
}

type Studentt struct {
	Human
	Grade int
	Age   int
}

func (s Studentt) Drink() {
	fmt.Println("student drinking...")
}

func (s Studentt) Drink1() {
	fmt.Println("student drinking...")
}

func Heal(b Being) {
	fmt.Println(b.IsLive)
}

type IHuman interface {
	Eat()
	Drink()
}
